﻿package luceneUtils;

import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

import jsonUtil.BooleanQuery0;
import jsonUtil.BooleanQuery1;
import jsonUtil.json;
import zhang.com.ssm.po.DocInfo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.CachingWrapperFilter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.SearcherFactory;
import org.apache.lucene.search.SearcherManager;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.search.WildcardQuery;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;
//import org.apache.lucene.search.QueryFilter;
/**
 * 对常用查询的一个封装
 * @author 王锐
 * @since 2016年12月8日
 */
public class PackQuery {

	Log log = LogFactory.getLog(PackQuery.class);
	
	// 分词器
	private Analyzer analyzer;
    private Query LastQuery=null;
	// 使用索引中的分词器
	public PackQuery() {
		analyzer =new IKAnalyzer(true);
	}
	// 使用自定义分词器
	public PackQuery(Analyzer analyzer) {
		this.analyzer = analyzer;
	}
  //实现查询
	public List<DocInfo> Query(String indexDir,String jsonStr) throws IOException, ParseException{
		List<DocInfo>list=new ArrayList<DocInfo>();
		Directory dir =FSDirectory.open(new File(indexDir));
        SearcherManager sm = new SearcherManager(dir,new  SearcherFactory());
		IndexSearcher indexSearcher = sm.acquire();
		Query q=getQuery(jsonStr);
       TopDocs hits = indexSearcher.search(q, 10);
       System.out.println("共找到"+hits.scoreDocs.length+"篇相关文献！");
	   for(ScoreDoc doc : hits.scoreDocs){
		   DocInfo doc1=new DocInfo();
		Document document = indexSearcher.doc(doc.doc);
		doc1.setTitle(document.get("title"));
		doc1.setAuthor(document.get("author"));
		doc1.setDocRes(document.get("docRes"));
		doc1.setPublishedTime(document.get("publishedTime"));
		list.add(doc1);
	}
	return list;
	    }
	//实现查询1,返回文献在数据库中的id
	public List<String> Query1(String indexDir,String jsonStr) throws IOException, ParseException{
		List<String>list=new ArrayList<String>();
		Directory dir =FSDirectory.open(new File(indexDir));
        SearcherManager sm = new SearcherManager(dir,new  SearcherFactory());
		IndexSearcher indexSearcher = sm.acquire();
		Query q=getQuery(jsonStr);
       TopDocs hits = indexSearcher.search(q, 10);
       System.out.println("共找到"+hits.scoreDocs.length+"篇相关文献！");
	   for(ScoreDoc doc : hits.scoreDocs){
		   DocInfo doc1=new DocInfo();
		Document document = indexSearcher.doc(doc.doc);
		list.add(document.get("id"));
	}
	return list;
	    }
    
    //在搜索oldqw的结果集中搜索qw.
    public Query searchInResult(String jsonStr) throws ParseException, IOException {
    	BooleanQuery query=new BooleanQuery();
		if(LastQuery!=null)
		{
	    query.add(LastQuery,Occur.MUST);
		query.add( getQuery(jsonStr),Occur.MUST);
		}
		else
		{
			System.out.println("您尚未作任何查询！");
		}
    	return query;                
    }
	/**
	 * 查询字符串匹配多个查询域
	 * @param key 查询关键字
	 * @param fields 查询域
	 * @return 返回查询对应的结果
	 * @throws ParseException
	 */
	public Query getMultiFieldQuery(String key, String[] fields) throws ParseException {
		MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_43, fields,analyzer);
		return parser.parse(key);
	}
	
	/**
	 * 查询字符串匹配单个查询域
	 * @param key 查询关键字
	 * @param field 查询域
	 * @return 返回查询对应结果
	 * @throws ParseException
	 */
	public Query getOneFieldQuery(String key, String field) throws ParseException{
		if(key == null || key.length() < 1){
			return null;
		}
		QueryParser parser = new QueryParser(Version.LUCENE_43, field,analyzer);
		return parser.parse(key);
	}
	public Query getOneFieldQuery(String key, String field,boolean flag) throws ParseException{
		Query query=null;
		if(key!=null&&!key.equals("")){
		QueryParser parser = new QueryParser(Version.LUCENE_43, field,analyzer);
		parser.setAllowLeadingWildcard(true);
		if(key.length()==1){
			query= parser.parse("*"+key+"*");
		}
		else
		{
		if(flag){
		query= parser.parse(key);
		}
		else
			query=parser.parse("\""+key+"\"");
		   System.out.println("\""+key+"\"");
		}
		}
		return query;
	}
	
	/**
	 * 查询字符串、多个查询域以及查询域在查询语句中的关系
	 * @param key 查询关键字
	 * @param fields 查询域
	 * @param occur
	 * @return
	 * @throws IOException
	 */
	public Query getBooleanQuery(String key, String[] fields, Occur[] occur) throws IOException{
		if(fields.length != occur.length){
			log.info("fields.length isn't equals occur.length, please check params!");
			return null;
		}
		//在lucene5.x中，BooleanQuery的实例化变成了BooleanQuery.Builder
		//BooleanQuery query = new BooleanQuery();
		BooleanQuery query = new BooleanQuery();
		TokenStream tokenStream = analyzer.tokenStream("", new StringReader(key));
		List<String> analyzerKeys = new ArrayList<String>();
		tokenStream.reset(); 
		while(tokenStream.incrementToken()){
			CharTermAttribute term = tokenStream.getAttribute(CharTermAttribute.class);
			analyzerKeys.add(term.toString());
		}
		for(int i = 0 ; i < fields.length ; i++){
			for(String analyzerKey : analyzerKeys){
				TermQuery termQuery = new TermQuery(new Term(fields[i],analyzerKey));
				query.add(termQuery,occur[i]);
			}
		}
		tokenStream.end();
		tokenStream.close();
		return query;
	}
	
	/**
	 * 组合多个查询，之间的关系有occur决定
	 * @param querys
	 * @param occurs
	 * @return
	 */
	public Query getBooleanQuery(List<Query> querys,List<Occur> occurs){
		if(querys.size() != occurs.size()){
			log.info("querys.size() isn't equals occurs.size(), please check params!");
			return null;
		}
		//在lucene5.x中，BooleanQuery的实例化变成了BooleanQuery.Builder
		//BooleanQuery query = new BooleanQuery();
		BooleanQuery query = new BooleanQuery();
		for(int i = 0 ; i < querys.size() ; i++){
			query.add(querys.get(i),occurs.get(i));
		}
		return query;
	}
	public Query getBooleanQuery(String field,String key1,Occur occur,String key2,boolean flag) throws ParseException{
		BooleanQuery Bquery = new BooleanQuery();
	   if(key1!=null&&!key1.equals("")){
		   Bquery.add(getOneFieldQuery(key1, field, flag), Occur.MUST);
	   }
	   System.out.println(key1+" "+field+" "+flag);
	   if(key2!=null&&!key2.equals("")){
		   Bquery.add(getOneFieldQuery(key2, field, flag),occur);
	   }
		return Bquery;
	}
	public Query getBooleanQuery(String field,String key1,String field1,String key2,String flag1,String flag2) throws ParseException{
		BooleanQuery Bquery = new BooleanQuery();
		boolean flag;
	   if(key1!=null&&!key1.equals("")){
		   if(flag1.equals("精确")){
			   flag=false;
		   }
		   else
			   flag=true;
		   Bquery.add(getOneFieldQuery(key1, field, flag), Occur.MUST);
	   }
	   if(key2!=null&&!key2.equals("")){
		   if(flag2.equals("精确")){
			   flag=false;
		   }
		   else
			   flag=true;
		   Bquery.add(getOneFieldQuery(key2, field1, flag),Occur.MUST);
	   }
	   if(Bquery.toString().length()==0){
		   Bquery=null;
	   }
		return Bquery;
	}
	List<Occur>getOccurs(String jsonStr){
		List<String>occurs=json.getOccur(jsonStr);
		List<Occur>occur=new ArrayList<Occur>();
		 for(int i=0;i<occurs.size();i++)
		 {
			 if(occurs.get(i).equals("and"))
				 occur.add(Occur.MUST);
			 else if(occurs.get(i).equals("or")){
				 occur.add(Occur.SHOULD);
			 }
			 else
				 occur.add(Occur.MUST_NOT);
		 }
		return occur;
	}
	public  Query getQuery(String  jsonStr){
		//String s="{\"query\":{\"booleanQuery\":[{\"theme1\":\"主题\",\"key1\":\"全文检索\",\"relation\":\"并含\",\"key2\":\"lucene\",\"special\":\"精确\"}],\"RangeQuery\":{\"start\":\"2013年\",\"end\":\"2016年\"},\"fieldQuery\":{\"res\":{\"value\":\"SCI\",\"special\":\"精确\"},\"fund\":{\"value\":\"\",\"special\":\"精确\"}},\"booleanQuery1\":[{\"autT\":\"作者\",\"key1\":\"\",\"special1\":\"精确\",\"key2\":\"\",\"special2\":\"精确\"}]},\"occur\":[\"and\",\"and\",\"and\",\"and\",\"and\"]}";
		BooleanQuery querys=new BooleanQuery();
		List<BooleanQuery0>list=json.getBooleanQuery(jsonStr);
		List<Occur>occurs=getOccurs(jsonStr);
		int i;
		for(i=0;i<list.size();i++){
			BooleanClause.Occur occur = null;
			BooleanClause.Occur occur1 = null;
			boolean flag;
		  if(!"".equals(list.get(i).getKey1())||!"".equals(list.get(i).getKey2())){
			  String relation=list.get(i).getRelation();
			  if(relation.equals("并含"))
			   occur=Occur.MUST;
			  if(relation.equals("或含"))
				   occur=Occur.SHOULD;
			  if(relation.equals("不含"))
				   occur=Occur.MUST_NOT;
			  if("精确".equals(list.get(i).getSpecial()))
				  flag=false;
			  else
				  flag=true;
			  try {
				  Query q=getBooleanQuery(list.get(i).getTheme1(),list.get(i).getKey1(),occur,list.get(i).getKey2(),flag);
				if(q!=null)
				  querys.add( q,occurs.get(i));
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		  }
		  System.out.println(querys.toString());
		  
		  Map<String,String> map=json.getRangeQuery(jsonStr);
		  String begin=map.get("start");
		  String end1=map.get("end");
		   if(!begin.equals("")&&!end1.equals("")){
			   int start=Integer.parseInt(map.get("start").split("年")[0]);
				int end=Integer.parseInt(map.get("end").split("年")[0]);
				if(end>=start)
			    	querys.add(getRangeQuery1(start,end,"publishedTime",true,true),occurs.get(i));
		   }
		   System.out.println(querys.toString());
		   
		i++;
	    List<Map<String,String>>map1=json.getFieldQuery(jsonStr);
		String value=map1.get(0).get("value");
		String special=map1.get(0).get("special");
		if(value!=null&&!"".equals(value)){
			 if("精确".equals(special))
				  flag=false;
			  else
				  flag=true;
			 try {
				 Query q=getOneFieldQuery(value,"docRes", flag);
				 if(q!=null)
				querys.add(q,occurs.get(i));
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		  System.out.println(querys.toString());
		  
		i++;
		 value=map1.get(1).get("value");
		special=map1.get(1).get("special");
		if(value!=null&&!"".equals(value)){
			 if("精确".equals(special))
				  flag=false;
			  else
				  flag=true;
			 try {
				 Query q=getOneFieldQuery(value,"fund", flag);
				 if(q!=null)
				querys.add(q,occurs.get(i));
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		  System.out.println(querys.toString());
		i++;
		List<BooleanQuery1>list1=json.getBooleanQuery1(jsonStr);
		for(int j=0;j<list1.size();j++){
			BooleanQuery1 b=list1.get(j);
			String key1=b.getKey1();
			String special1=b.getSpecial1();
			String key2=b.getKey2();
			String special2=b.getSpecial2();
		    try {
				Query q=getBooleanQuery("author",key1,"AU",key2,special1,special2);
			
				if(q!=null)
					querys.add(q, occurs.get(i));
			} catch (ParseException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			i++;
		}
		}
		System.out.println(querys.toString());
		LastQuery=querys;
		return querys;
	}
	/**
	 * StringField属性的搜索
	 * @param value
	 * @param fieldName
	 * @return
	 */
	public Query getStringFieldQuery(String value,String fieldName){
		return new TermQuery(new Term(fieldName,value));
	}
	
	/**
	 * 多个StringField属性的搜索
	 * @param value
	 * @param fields
	 * @param occur
	 * @return
	 */
	public Query getStringFieldQuery(String[] value, String[] fields, Occur occur){
		if(value == null || fields == null || value.length != fields.length){
			return null;
		}
		List<Query> querys = new ArrayList<Query>();
		List<Occur> occurs = new ArrayList<Occur>();
		for(int i = 0 ; i < fields.length ; i++){
			querys.add(getStringFieldQuery(value[i], fields[i]));
			occurs.add(occur);
		}
		return getBooleanQuery(querys, occurs);
	}
	
	/**
	 * key开头的查询字符串，和单个域匹配
	 * @param key
	 * @param field
	 * @return
	 */
	private Query getStartQuery(String key, String field) {
		if(key == null || field.length() < 1){
			return null;
		}
		return new PrefixQuery(new Term(field,key));
	}
	
	/**
	 * key开头的查询字符串，和多个域匹配，每个域之间的关系由occur决定
	 * @param key
	 * @param fields
	 * @param occur
	 * @return
	 */
	public Query getStartQuery(String key,String[] fields,Occur occur){
		if(key == null || fields.length < 1){
			return null;
		}
		List<Query> querys = new ArrayList<Query>();
		List<Occur> occurs = new ArrayList<Occur>();
		for(String field : fields){
			querys.add(getStartQuery(key, field));
			occurs.add(occur);
		}
		return getBooleanQuery(querys, occurs);
	}
	
	/**
	 * key开头的查询字符串，和多个域匹配，每个域之间的关系由occur决定
	 * @param key
	 * @param fields
	 * @param occur
	 * @param isAnalyzer 是否分词
	 * @return
	 * @throws IOException 
	 */
	public Query getStartQuery(String key,String[] fields,Occur occur,boolean isAnalyzer) throws IOException{
		if(key == null || fields.length < 1){
			return null;
		}
		List<Query> querys = new ArrayList<Query>();
		List<Occur> occurs = new ArrayList<Occur>();
		//判断是否使用分词，如果分词就先分词，再搜索
		if (isAnalyzer) {
			TokenStream tokenStream = analyzer.tokenStream("", new StringReader(key));
			List<String> analyzerKeys = new ArrayList<String>();
			tokenStream.reset(); 
			while(tokenStream.incrementToken()){
				CharTermAttribute term = tokenStream.getAttribute(CharTermAttribute.class);
				analyzerKeys.add(term.toString());
			}
			for(int i = 0 ; i < fields.length ; i++){
				for(String analyzerKey : analyzerKeys){
					querys.add(getStartQuery(analyzerKey, fields[i]));
					occurs.add(occur);
				}
			}
			tokenStream.end();
			tokenStream.close();
		}else{
			for(String field : fields){
				querys.add(getStartQuery(key, field));
				occurs.add(occur);
			}
		}
		return getBooleanQuery(querys, occurs);
	}
	
	/**
	 * key开头的查询字符串，和多个域匹配，每个域之间的关系Occur.SHOULD
	 * @param key
	 * @param fields
	 * @param isAnalyzer 是否支持分词
	 * @return
	 * @throws IOException 
	 */
	public Query getStartQuery(String key,String[] fields,boolean isAnalyzer) throws IOException{
		return getStartQuery(key, fields, Occur.SHOULD, isAnalyzer);
	}
	
	/**
	 * 自定义每个次元之间的最大距离
	 * @param key
	 * @param field
	 * @param slop
	 * @return
	 */
	public Query getPhraseQuery(String key, String field, int slop){
		if(key == null || key.length() < 1){
			return null;
		}
		StringReader reader = new StringReader(key);
		//在lucene5.x中，PhraseQuery的实例化变成了PhraseQuery.Builder
		//PhraseQuery query = new PhraseQuery();
		PhraseQuery query = new PhraseQuery();
		query.setSlop(slop);
		try{
			TokenStream tokenStream = this.analyzer.tokenStream(field, reader);
			tokenStream.reset();
			CharTermAttribute term = tokenStream.getAttribute(CharTermAttribute.class);
			while(tokenStream.incrementToken()){
				query.add(new Term(field,term.toString()));
			}
			tokenStream.end();
			tokenStream.close();
		}catch(Exception e){
			e.printStackTrace();
			return null;
		} finally {
			reader.close();
		}
		//return query;
		return query;
	}
	
	/**
	 * 自定义每个次元之间的最大距离，查询多个域，每个域之间的关系由occur决定
	 * @param key
	 * @param fields
	 * @param slop
	 * @param occur
	 * @return
	 */
	public Query getPhraseQuery(String key,String[] fields, int slop, Occur occur){
		if(key == null || key.length() < 1){
			return null;
		}
		List<Query> querys = new ArrayList<Query>();
		List<Occur> occurs = new ArrayList<Occur>();
		for(String field : fields){
			querys.add(getPhraseQuery(key, field, slop));
			occurs.add(occur);
		}
		return getBooleanQuery(querys, occurs);
	}
	
	/**
	 * 自定义每个次元之间的最大距离，查询多个域，每个域之间的关系为Occur.SHOULD
	 * @param key
	 * @param fields
	 * @param slop
	 * @return
	 */
	public Query getPhraseQuery(String key, String[] fields,int slop){
		return getPhraseQuery(key, fields, slop, Occur.SHOULD);
	}

	/**
	 * 通配符搜索，例如:getWildcardQuery("a*thor", "field") 
	 * @param key
	 * @param field
	 * @return
	 */
	public Query getWildcardQuery(String key,String field){
		if(key == null || key.length() < 1){
			return null;
		}
		return new WildcardQuery(new Term(field,key));
	}
	
	/**
	 * 通配符搜索，域之间的关系为occur
	 * @param key
	 * @param fields
	 * @param occer
	 * @return
	 */
	public Query getWidcardQuery(String key,String[] fields,Occur occer){
		if(key == null || key.length() < 1){
			return null;
		}
		List<Query> querys = new ArrayList<Query>();
		List<Occur> occurs = new ArrayList<Occur>();
		for(String field : fields){
			querys.add(getWildcardQuery(key, field));
			occurs.add(occer);
		}
		return getBooleanQuery(querys, occurs);
	}
	
	/**
	 * 通配符搜索，域之间的关系为Occur.SHOULD
	 * @param key
	 * @param fields
	 * @return
	 */
	public Query getWildcardQuery(String key, String[] fields){
		return getWidcardQuery(key, fields, Occur.SHOULD);
	}
	
	/**
	 * 范围搜索
	 * @param keyStart
	 * @param keyEnd
	 * @param field
	 * @param includeStart
	 * @param includeEnd
	 * @return
	 */
	public Query getRangeQuery(String keyStart,String keyEnd,String field, boolean includeStart,boolean includeEnd){
		return TermRangeQuery.newStringRange(field, keyStart, keyEnd, includeStart, includeEnd);
	}
	
	/**
	 * 范围搜索
	 * @param min
	 * @param max
	 * @param field
	 * @param includeMin
	 * @param includeMax
	 * @return
	 */
	public Query getRangeQuery1(int min,int max,String field,boolean includeMin,boolean includeMax){
		return NumericRangeQuery.newIntRange(field, min, max, includeMin, includeMax);
	}
	public Query getRangeQuery(long min,long max,String field,boolean includeMin,boolean includeMax){
		return NumericRangeQuery.newLongRange(field, min, max, includeMin, includeMax);
	}
	/**
	 * 范围搜索
	 * @param min
	 * @param max
	 * @param field
	 * @param includeMin
	 * @param includeMax
	 * @return
	 */
	public Query getRangeQuery(float min,float max,String field,boolean includeMin,boolean includeMax){
		return NumericRangeQuery.newFloatRange(field, min, max, includeMin, includeMax);
	}
	
	/**
	 * 范围搜索
	 * @param min
	 * @param max
	 * @param field
	 * @param includeMin
	 * @param includeMax
	 * @return
	 */
	public Query getRangeQuery(double min,double max,String field,boolean includeMin,boolean includeMax){
		return NumericRangeQuery.newDoubleRange(field, min, max, includeMin, includeMax);
	}
	
}
